﻿#region Using

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Vk.Methods.Common;

#endregion

namespace Vk.Methods.Places
{
    /// <summary>
    ///     Места
    /// </summary>
    public class Places
    {
        private readonly Api _api;

        public Places(Api api)
        {
            _api = api;
        }

        /// <summary>
        ///     Добавляет новое место в базу географических мест. Созданное место будет выводиться в поиске по местам только тому,
        ///     кто его добавил.
        /// </summary>
        /// <param name="title">Название нового места.</param>
        /// <param name="latitude">Географическая широта нового места, заданная в градусах (от -90 до 90).</param>
        /// <param name="longitude">Географическая долгота нового места, заданная в градусах (от -180 до 180).</param>
        /// <param name="type">Идентификатор типа нового места, полученный методом places.getTypes.</param>
        /// <param name="country">Идентификатор страны нового места, полученный методом places.getCountries.</param>
        /// <param name="city">Идентификатор города нового места, полученный методом places.getCities.</param>
        /// <param name="address">Строка с адресом нового места (например, Невский просп. 1).</param>
        /// <returns>В случае успешного создания места метод возвратит идентификатор созданного места (pid). </returns>
        public async Task<int> Add(string title,
                                   double latitude,
                                   double longitude,
                                   int type,
                                   int? country = null,
                                   int? city = null,
                                   string address = null)
        {
            var query = new ApiQuery("places.add")
                .Add("title", title)
                .Add("latitude", latitude)
                .Add("longitude", longitude)
                .Add("type", type)
                .Add("country", country)
                .Add("city", city)
                .Add("address", address);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Int("id").HasValue ? result.Int("id").Value : -1;
        }

        /// <summary>
        ///     Отмечает пользователя в указанном месте.
        /// </summary>
        /// <param name="placeId">Идентификатор места.</param>
        /// <param name="text">Комментарий к отметке длиной до 255 символов (переводы строк не поддерживаются).</param>
        /// <param name="latitude">Географическая широта отметки, заданная в градусах (от -90 до 90).</param>
        /// <param name="longitude">Географическая долгота отметки, заданная в градусах (от -180 до 180).</param>
        /// <param name="services">
        ///     Список сервисов или сайтов, на которые необходимо экспортировать отметку, в случае если
        ///     пользователь настроил соответствующую опцию. Например twitter, facebook.
        /// </param>
        /// <param name="friendsOnly">
        ///     1 - отметка будет доступна только друзьям, 0 - всем пользователям. По умолчанию публикуемые
        ///     отметки доступны всем пользователям.
        /// </param>
        /// <returns>В случае успешного создания места метод возвратит идентификатор созданной отметки (сid). </returns>
        public async Task<int> Checkin(int placeId,
                                       string text = null,
                                       int? latitude = null,
                                       int? longitude = null,
                                       IEnumerable<string> services = null,
                                       bool? friendsOnly = null)
        {
            var query = new ApiQuery("places.checkin")
                .Add("place_id", placeId)
                .Add("text", text)
                .Add("latitude", latitude)
                .Add("longitude", longitude)
                .Add("services", services)
                .Add("friends_only", friendsOnly);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Int().HasValue ? result.Int().Value : -1;
        }

        /// <summary>
        ///     Возвращает информацию о местах по их идентификаторам.
        /// </summary>
        /// <param name="places">Перечисленные через запятую идентификаторы мест.</param>
        public async Task<List<Place>> GetById(IEnumerable<int> places = null)
        {
            var query = new ApiQuery("places.getById")
                .Add("places", places);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("place");
                if (nodes != null && nodes.Any())
                    return nodes.Select(x => new Place(x)).ToList();
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список отметок пользователей в местах, согласно заданным параметрам.
        /// </summary>
        /// <param name="latitude">Географическая широта исходной точки поиска, заданная в градусах (от -90 до 90).</param>
        /// <param name="longitude">Географическая долгота исходной точки поиска, заданная в градусах (от -180 до 180).</param>
        /// <param name="place">Идентификатор места. Игнорируется, если указаны latitude и longitude.</param>
        /// <param name="userId">Идентификатор пользователя. Игнорируется, если указаны latitude и longitude или place.</param>
        /// <param name="offset">
        ///     Смещение относительно первой отметки для выборки определенного подмножества. Игнорируется, если
        ///     установлен ненулевой timestamp.
        /// </param>
        /// <param name="count">Количество возвращаемых отметок (максимум 50). Игнорируется, если установлен ненулевой timestamp.</param>
        /// <param name="timestamp">Указывает, что нужно вернуть только те отметки, которые были созданы после заданного timestamp.</param>
        /// <param name="friendsOnly">
        ///     Указывает, что следует выводить только отметки друзей, если заданы географические координаты.
        ///     Игнорируется, если не заданы параметры latitude и longitude.
        /// </param>
        /// <param name="needPlaces">
        ///     Указывает, следует ли возвращать информацию о месте в котором была сделана отметка.
        ///     Игнорируется, если указан параметр place.
        /// </param>
        public async Task<ListCount<Checkin>> GetCheckins(int? latitude = null,
                                                          int? longitude = null,
                                                          int? place = null,
                                                          int? userId = null,
                                                          int? offset = null,
                                                          int? count = null,
                                                          int? timestamp = null,
                                                          bool? friendsOnly = null,
                                                          bool? needPlaces = null)
        {
            var query = new ApiQuery("places.getCheckins")
                .Add("latitude", latitude)
                .Add("longitude", longitude)
                .Add("place", place)
                .Add("uid", userId)
                .Add("offset", offset)
                .Add("count", count)
                .Add("timestamp", timestamp)
                .Add("friends_only", friendsOnly)
                .Add("need_places", needPlaces);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("//checkin");
                if (nodes != null && nodes.Any())
                    return new ListCount<Checkin>(result.Int("count").Value, nodes.Select(x => new Checkin(x)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список всех возможных типов мест.
        /// </summary>
        /// <returns>Возвращает массив всех возможных типов мест, каждый из объектов которого содержит поля id, title и icon. </returns>
        public async Task<List<PlaceType>> GetTypes()
        {
            var query = new ApiQuery("places.getType");

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("type");
                if (nodes != null && nodes.Any())
                    return nodes.Select(x => new PlaceType(x)).ToList();
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список мест, найденных по заданным условиям поиска. Поиск производится среди мест, добавленных
        ///     модераторами сайта и текущим пользователем. Места в списке расположены в порядке увеличения дистанции от исходной
        ///     точки поиска.
        /// </summary>
        /// <param name="latitude">
        ///     Географическая широта точки, в радиусе которой необходимо производить поиск, заданная в градусах
        ///     (от -90 до 90).
        /// </param>
        /// <param name="longitude">
        ///     Географическая долгота точки, в радиусе которой необходимо производить поиск, заданная в
        ///     градусах (от -180 до 180).
        /// </param>
        /// <param name="city">Идентификатор города</param>
        /// <param name="count">Количество мест, информацию о которых необходимо вернуть</param>
        /// <param name="offset">Смещение, необходимое для выборки определенного подмножества результатов поиска</param>
        /// <param name="q">Строка поискового запроса.</param>
        /// <param name="radius">Тип радиуса зоны поиска</param>
        public async Task<ListCount<Place>> Search(int latitude,
                                                   int longitude,
                                                   string q = null,
                                                   int? city = null,
                                                   int? offset = null,
                                                   int? count = null,
                                                   ApiEnum<SearchRadius> radius = null)
        {
            var query = new ApiQuery("places.search")
                .Add("latitude", latitude)
                .Add("longitude", longitude)
                .Add("q", q)
                .Add("radius", radius)
                .Add("count", count)
                .Add("offset", offset)
                .Add("city", city);

            var result = await _api.Manager.Execute(query);

            if (_api.Manager.MethodSuccessed)
            {
                var nodes = result.SelectNodes("//place");
                if (nodes != null && nodes.Any())
                    return new ListCount<Place>(result.Int("count").Value, nodes.Select(x => new Place(x)).ToList());
            }
            return null;
        }
    }
}